package lumis.portal.progress;

import java.io.ByteArrayOutputStream;
import java.io.UnsupportedEncodingException;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.GregorianCalendar;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.w3c.dom.Document;

import lumis.portal.PortalException;
import lumis.portal.controller.ControllerException;
import lumis.portal.controller.ControllerHtml;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.manager.ManagerFactoryInternal;
import lumis.portal.progress.ProgressConfig.Type;
import lumis.portal.serialization.SerializationException;
import lumis.portal.transaction.PortalTransactionFactory;
import lumis.util.IResource;
import lumis.util.ITransaction;
import lumis.util.TextUtil;
import lumis.util.XmlUtil;

/**
 * HTML Controller that returns progress messages for a process to the client
 *
 * @version $Revision: 13019 $ $Date: 2011-05-20 10:38:47 -0300 (Fri, 20 May 2011) $
 * @since 4.0.7
 */
public class ProgressControllerHtml extends ControllerHtml
{
	private int queryIntervalMillis = 1000;

	private long timeoutMillis = 1 * 60 * 60 * 1000;

	public ProgressControllerHtml(HttpServletRequest request, HttpServletResponse response) throws ControllerException, PortalException
	{
		super(request, response);
	}

	public void handleRequest() throws Exception
	{
		try
		{
			String processId = request.getParameter("lumProcessId");
			
			if (processId == null)
				return;
			
			String abort = request.getParameter("lumAbort");
			String context = request.getParameter("lumContext");
			
			if (abort != null)
			{
				ManagerFactoryInternal.getProgressManager().addOrUpdate(new ProgressConfig(processId, 0, 0, 0, 0, null, Type.ABORT, false));
			}
			else
			{
				IProgressManager progressManager = ManagerFactoryInternal.getProgressManager();
	
				// expire old progress entries
				GregorianCalendar expirationDate = new GregorianCalendar();
				expirationDate.add(Calendar.DAY_OF_MONTH, -1);
				progressManager.deleteExpired(new Timestamp(expirationDate.getTimeInMillis()));
	
				out.write("<html><head>");
				out.write("<link type=\"text/css\" rel=\"stylesheet\" href=\"../../../portal/client/stylesheet/portal.css\"/>");
				out.write("<link type=\"text/css\" rel=\"stylesheet\" href=\"../../../portal/client/stylesheet/portaladmin.css\"/>");
				out.write("<link type=\"text/css\" rel=\"stylesheet\" href=\"../../../portal/client/stylesheet/progress.css\"/>");
				out.write("<style>");
				if ("serialization".equals(context))
					out.write(".cLumProgressSerializationOnly {display:inline;}");
				else
					out.write(".cLumProgressSerializationOnly {display:none;}");
				
				
				out.write("</style>");
				out.write("</head><body style=\"margin-top:0\">");
				out.write("<script type=\"text/javascript\" src=\"../../../portal/progress/client/script/MessageProgress.js\"/>");
	
				long lastAliveTime = System.currentTimeMillis();
				boolean isLastMessage = false;
				Timestamp startDate = null;
				do
				{
					boolean noMessages = true;
					for (ProgressConfig progressConfig : progressManager.getByProcessId(processId, startDate))
					{
						String elapsedTime = formatTime(progressConfig.getElapsedTime());
						long remainingTimeMillis = progressConfig.calculateRemainingTimeMillis();
						String estimatedTime = remainingTimeMillis == -1 ? "--:--:--" : formatTime(remainingTimeMillis); 
						if (progressConfig.isLastMessage() || (!progressConfig.getType().equals(Type.STEP) && !progressConfig.getType().equals(Type.SCRIPT)))
						{
							String innerProgressId = progressConfig.getInnerProgressId() == null ? "" : "<innerProgressId>" + progressConfig.getInnerProgressId() + "</innerProgressId>"; 
							String messageXml = "<message><type>" + Integer.toString(progressConfig.getType().toInt()) + "</type><typeLegend>STR_" + progressConfig.getType().toString() + "</typeLegend><content>" + XmlUtil.encodeXml(progressConfig.getMessage())
									+ "</content>" + innerProgressId + "</message>";
	
							out.write(formatMessage(messageXml));
						}
						if (progressConfig.isLastMessage() || progressConfig.getType().equals(Type.STEP) || progressConfig.getType().equals(Type.WARNING) || progressConfig.getType().equals(Type.INFO))
						{
							if (progressConfig.isLastMessage())
							{
								progressConfig.setItemMax(0);
								progressConfig.setItemValue(0);
								progressConfig.setOverallMax(0);
								progressConfig.setOverallValue(0);
							}
							out.write("<script type=\"text/javascript\">if (window.parent != null && window.parent.showProgress != null) window.parent.showProgress(");
							out.write(Integer.toString(progressConfig.getOverallMax()));
							out.write(",");
							out.write(Integer.toString(progressConfig.getOverallValue()));
							out.write(",");
							out.write(Integer.toString(progressConfig.getItemMax()));
							out.write(",");
							out.write(Integer.toString(progressConfig.getItemValue()));
							if (progressConfig.getMessage() != null && progressConfig.getMessage().length() < 100 && !progressConfig.getType().equals(Type.SCRIPT))
							{
								out.write(",'");
								out.write(TextUtil.stringReplace(progressConfig.getMessage(), "'", "\\'"));
								out.write("'");
							}
							else
							{
								out.write(",null");
							}
							out.write(",'");
							out.write(elapsedTime);
							out.write("','");
							out.write(estimatedTime);
							out.write("'); </script><noscript><!--Lumis portal script--></noscript>");
						}
						if (progressConfig.isLastMessage())
							out.write("<script type=\"text/javascript\">if (window.parent != null && window.parent.progressEnd != null) window.parent.progressEnd();</script><noscript><!--Lumis portal script--></noscript>");
						if (progressConfig.getType().equals(Type.SCRIPT))
						{
							out.write("<script type=\"text/javascript\">");
							out.write(progressConfig.getMessage());
							out.write("</script><noscript><!--Lumis portal script--></noscript>");
						}
						out.flush();
	
						isLastMessage = isLastMessage || progressConfig.isLastMessage();
						startDate = progressConfig.getCreatedDateTime();
						noMessages = false;
					}
					if (noMessages)
						Thread.sleep(queryIntervalMillis);
					else
						lastAliveTime = System.currentTimeMillis();
				}
				while ((System.currentTimeMillis() - lastAliveTime) < timeoutMillis && !isLastMessage);
				out.write("</BODY>");
				out.write("</HTML>");
				progressManager.deleteByProcessId(processId);
			}
		}
		catch (Exception e)
		{
			out.write("<script type=\"text/javascript\">alert('"+TextUtil.escapeJs(e.getLocalizedMessage())+"')</script>");
		}
		}

	private String formatTime(long timeMillis) throws PortalException
	{
		IResource resource = new ProgressStringResource();
		String timePattern = ManagerFactory.getLocalizationManager().localize("TIME_PATTERN", resource);
		int seconds = Math.round(timeMillis / 1000);
		int minutes = (int)Math.floor(seconds / 60);
		seconds -= minutes * 60;
		int hours = (int)Math.floor(minutes / 60);
		minutes -= hours * 60;
		timePattern = TextUtil.stringReplace(timePattern, "h", Integer.toString(hours));
		timePattern = TextUtil.stringReplace(timePattern, "mm", TextUtil.leftPad(Integer.toString(minutes), 2, '0'));
		timePattern = TextUtil.stringReplace(timePattern, "ss", TextUtil.leftPad(Integer.toString(seconds), 2, '0'));
		return timePattern;
	}
	
	private String formatMessage(String messageXml) throws PortalException
	{
		String localizedMessage = null;
		Document messageDoc = XmlUtil.getDocument(messageXml);
		String xslFilePath = "lumis/portal/progress/style/ProgressMessage.xsl";
		IResource resource = new ProgressStringResource();
		ByteArrayOutputStream transformedStream = null;
		String message = null;
		try
		{
			Transformer transformer;
			ITransaction transaction = PortalTransactionFactory.createTransaction();
			try
			{
				transaction.begin();
				transformer = ManagerFactoryInternal.getXslCacheManager().getTransformer(sessionConfig, xslFilePath, transaction);
				transaction.commit();
			}
			finally
			{
				transaction.dispose();
			}
			
			DOMSource domSource = new DOMSource(messageDoc);
			transformedStream = new ByteArrayOutputStream();
			transformer.transform(domSource, new StreamResult(transformedStream));
			try
			{
				message = transformedStream.toString("UTF-8");
			}
			catch (UnsupportedEncodingException e)
			{
				throw new PortalException("STR_ERROR_ENCODING_XML", resource, e);
			}
		}
		catch (TransformerException e)
		{
			throw new PortalException("STR_ERROR_TRANSFORMING_PROGRESS_MESSAGE;" + xslFilePath, resource, e);
		}
		finally
		{
			if (transformedStream != null)
			{
				try
				{
					transformedStream.close();
				}
				catch (Exception e)
				{
				}
			}
		}
		try
		{
			if (message != null)
			{
				localizedMessage = ManagerFactory.getLocalizationManager().localize(message, resource, sessionConfig.getLocale());
			}
		}
		catch (Exception e)
		{
			throw new SerializationException("STR_ERROR_TRANSFORMING_PROGRESS_MESSAGE", e);
		}

		return localizedMessage;
	}
}
